var http = require('http');
var fs = require('fs');

// 来自：https://www.cnblogs.com/accordion/p/4615637.html
// 并且，还有更加高级的功能，再看。

var helper = function(fn) {
  // 用于存储回调函数，该函数复用于所有用于helper处理的异步函数
  // 关键就在于这里，feed 其实就是异步的回调函数；
  // 通过 helper(asyncFun), 此时的回调函数形式是错误的，而且 feed 是undefined；
  // 但是当调用回调函数时，本次轮回结束，导致 feed 就有值了，所以 ddd 中的 feed.apply 可以正确执行；
  // 也就得到了正确的结果；
  var feed;
  /**
   * 执行次序分析：
   *    helper的参数fn是一个异步函数，通过helper的处理，返回一个含有内部处理逻辑
   *    的函数，该函数封装了所需参数和可能的回调函数feed，并且返回一个设置feed的函数。
   *
   *    在具体的使用中，通过helper函数封装fs.readFile，获取readFile。当执行第一个
   *    片段时，首先将所有的参数（包括feed）合并到args，并执行异步调用返回处理函数；此时
   *    我们用获取的返回函数设置回调函数，进而影响到args中的最后一项的函数
   */

  return function aaa(){
    var args = [].slice.call(arguments);

    args.push(function ddd(){
      if(feed) {
        console.log(feed); // [Function: ccc]
        feed.apply(null,arguments);
      }
      //console.log(feed)
    });
    console.log('args.length', args.length);
    console.log('args[0]', args[0]);
    console.log('args[1]', args[1]);
    console.log('args[2]', args[2]);
    fn.apply(null, args);
    console.log('fn执行中');
    // 返回一个函数，用于给yield之前的变量赋值
    return function bbb(fn){
      feed = fn;
    }
  }
};

var flow = function(gfn) {
  var generator = gfn();
  next();

  function next(data){
    console.log('next--data');
    console.log(typeof data);
    generator.ret = generator.next(data);
    console.log(generator.ret);
    console.log(generator.ret.value);// [Function: bbb]
    if(generator.ret.done){
      return;
    }
    generator.ret.value(
      function ccc(error,d){
        console.log('ccc');
        if(error)
          throw error;
        console.log('error', error);
        console.log('d', d);
        next.apply(null,[].slice.call(arguments,1));
      }
    );
  }
};

// console.log(global);

http.createServer(function (request, response) {
  if (request.url.indexOf('favicon') > -1) {
    response.writeHead(200, {'Content-Type': 'text/plain'});
    response.end('Hello World\n');
  } else {
    
    // 使用 fs.readFile 异步读取文件示例
    /*
    console.log('开始读取');
    fs.readFile('./zepto.js', 'utf8', function(err, data){
      if (err) console.log('err');
      console.log(data)
      console.log('ok');
    });
    console.log('哈哈');
    结果是：
    开始读取
    哈哈
    data 就是 zepto.js 内容
    ok
    */
    
    flow(function *(){
      var readFile = helper(fs.readFile);
      console.log('readFile');// 生成器函数执行时，函数体先不执行；所以看到先打印 next--data和它下面的typeof data的值，然后generator.next(data) 执行时才打印这里的 readFile；
      console.log(readFile);// [Function: aaa]
      //console.log(typeof readFile); // function
      // yield readFile('./zepto2.js', 'utf8');
      var t1 = yield readFile('./zepto2.js', 'utf8');
      console.log('t1');
      console.log(typeof t1); // string 文件的内容字符串
      console.log(t1);
      var t2 = yield readFile(t1, 'utf8'); // 这样就要求，上面读取的文件中的内容，是某个文件，这里才能正确执行。一般来说执行一次readFile才对。
      console.log('t2');
      console.log(t2);
    });

    // 发送 HTTP 头部 
    // HTTP 状态值: 200 : OK
    // 内容类型: text/plain
    response.writeHead(200, {'Content-Type': 'text/plain'});
    // 发送响应数据 "Hello World"
    response.end('Hello World\n');
  }
}).listen(8888);

// 终端打印如下信息
console.log('Server running at http://127.0.0.1:8888/');

/*
生成器函数 gfn 执行，函数体不执行，
执行 next();
此时 data 没有，所以是 undefined；
执行 generator.next(undefined); // 第一次执行这个，参数不管是什么，都是无用的。
【此时，gfn 的函数体执行，
执行 helper(fs.readFile)
得到变量 readFile 就是 aaa 函数；
然后执行 readFile('./zepto2.js', 'utf8')；
就是给 aaa() 传递了两个参数；
aaa 内的 args 长度是 3，分别是 文件名、编码和函数 ddd；
然后 fs.readFile.apply(); 执行；
关键就在这里，这是一个异步操作，不管文件的内容是多少，args 的第三个参数总是等待事件机制中的本轮结束后才能被调用；
也就是说，会先执行 return bbb，然后在某个时机执行回调ddd；
当执行完 return bbb，
generator.ret = generator.next(undefined); 也就有了结果：
generator.ret.done 肯定是false，因为是第一次调用 generator.next();
generator.ret.value 正是 bbb 函数；
然后 bbb 就执行了，参数是函数 ccc；
bbb的执行导致 feed = ccc；
本轮事件结束，循环机制适时执行回调ddd；
此时 feed 就是  ccc，也正是 fs.readFile 的回调函数的形式；
当正确的时候，err 是null，d就是文件内容；
ccc 执行到最后，next.apply 执行，传递给 next(data) 的参数正是 ccc 函数的第二个参数 d--文件内容；
next.apply 执行，generator.netx(data); 这个参数data 就是文件内容；
于是 t1 就是 data，就是文件内容；
一直到 yield 停止；】
 */
